import axios from "axios";
import CryptoJS from "crypto-js";
import forge from "node-forge"; // 引入 forge 库

export let PUBLIC_KEY = null;
export let IS_ENCRY_REQ_PARAMS = false;

/**
 * 获取RSA公钥
 */
export async function initRsaPublicKey() {
  try {
    const res = await axios.get(process.env.VUE_APP_BASE_API + '/rsa/publicKey');
    if (res.data.code == 200 && res.data.data) {
      PUBLIC_KEY = res.data.data['PK'];
      IS_ENCRY_REQ_PARAMS = res.data.data['isEncryReqParams'];
    } else {
      throw new Error('RSA公钥获取失败，响应码：' + res.data.code + '，数据：' + res.data.data);
    }
  } catch (error) {
    console.error('RSA公钥初始化失败：', error);
    throw error;
  }
}

// ------------------------------ 基础工具函数------------------------------
function base64Encode(strOrBytes) {
  if (typeof strOrBytes === 'string') {
    return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(strOrBytes));
  } else if (strOrBytes instanceof Uint8Array) {
    return CryptoJS.enc.Base64.stringify(CryptoJS.lib.WordArray.create(strOrBytes));
  }
  return '';
}

function base64Decode(base64Str) {
  const wordArray = CryptoJS.enc.Base64.parse(base64Str);
  const bytes = wordArray.toArray();
  return new Uint8Array(bytes);
}

// ------------------------------ 关键：RSA-OAEP-SHA256-MGF1-SHA256 加密------------------------------
/**
 * RSA加密：OAEP填充 + 消息哈希SHA256 + MGF1哈希SHA256
 * @param {string} plainText 待加密明文（AES密钥+IV）
 * @param {string} publicKeyPEM PEM格式RSA公钥（X.509）
 * @returns {string} Base64编码的RSA密文
 */
function rsaEncryptKeyNative(plainText, publicKeyPEM) {
  try {
    // 1. 解析 PEM 格式公钥（forge 支持直接解析 X.509 格式）
    const publicKey = forge.pki.publicKeyFromPem(publicKeyPEM);

    // 2. 明文转换为 UTF-8 字节数组
    const plainBytes = forge.util.encodeUtf8(plainText);

    // 3. 配置 OAEP 参数
    const oaepParams = {
      md: forge.md.sha256.create(), // 消息哈希：SHA256
      mgf1: {
        md: forge.md.sha256.create() // MGF1 哈希：SHA256
      },
      label: null // 后端未设置 label，此处设为 null
    };

    // 4. 执行 RSA-OAEP 加密（返回 forge 二进制格式）
    const encryptedBytes = publicKey.encrypt(plainBytes, 'RSA-OAEP', oaepParams);

    // 5. 加密结果转换为 Base64 编码
    const encryptedBase64 = forge.util.encode64(encryptedBytes);

    return encryptedBase64.replace(/\s+/g, '');
  } catch (error) {
    console.error('RSA加密失败：', error);
    throw new Error('RSA加密异常：' + error.message);
  }
}

// ------------------------------ AES工具函数 ------------------------------
const AESUtils = {
  encrypt: function(plainText, keyBase64, ivBase64) {
    const key = CryptoJS.enc.Base64.parse(keyBase64.replace(/\s+/g, ''));
    const iv = CryptoJS.enc.Base64.parse(ivBase64.replace(/\s+/g, ''));

    const encrypted = CryptoJS.AES.encrypt(plainText, key, {
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7 // 与后端 PKCS5 等价
    });

    return encrypted.ciphertext.toString(CryptoJS.enc.Base64).replace(/\s+/g, '');
  },

  generateKeyAndIv: function() {
    const key = CryptoJS.lib.WordArray.random(16); // 128位 AES 密钥
    const iv = CryptoJS.lib.WordArray.random(16); // 16字节 IV
    return {
      aesKey: key.toString(CryptoJS.enc.Base64).replace(/\s+/g, ''),
      aesIv: iv.toString(CryptoJS.enc.Base64).replace(/\s+/g, '')
    };
  }
};

// ------------------------------ 混合加密核心函数 ------------------------------
export async function encrypt(plainText, rsaPublicKeyPEM) {
  if (!plainText) {
    return {};
  }
  rsaPublicKeyPEM = rsaPublicKeyPEM || PUBLIC_KEY;
  try {
    const { aesKey, aesIv } = AESUtils.generateKeyAndIv();
    const encryptedText = AESUtils.encrypt(plainText, aesKey, aesIv);
    const aesKeyIv = `${aesKey}|${aesIv}`;
    const encryptedKey = rsaEncryptKeyNative(aesKeyIv, rsaPublicKeyPEM);
    return { encryptedKey, encryptedText };
  } catch (error) {
    console.error("混合加密失败：", error);
    throw new Error(`混合加密异常：${error.message}`);
  }
}

// ------------------------------ 文件上传加密 ------------------------------
async function encryptFileBlob(fileBlob, keyBase64, ivBase64) {
  const arrayBuffer = await fileBlob.arrayBuffer();
  const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);
  const key = CryptoJS.enc.Base64.parse(keyBase64.replace(/\s+/g, ''));
  const iv = CryptoJS.enc.Base64.parse(ivBase64.replace(/\s+/g, ''));
  const encrypted = CryptoJS.AES.encrypt(wordArray, key, {
    iv: iv,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  });
  return new Uint8Array(encrypted.ciphertext.words.reduce((arr, word) => {
    arr.push((word >> 24) & 0xff);
    arr.push((word >> 16) & 0xff);
    arr.push((word >> 8) & 0xff);
    arr.push(word & 0xff);
    return arr;
  }, []));
}

export async function encryptFileForUpload(fileBlob, filename, extraFields = {}) {
  if (!fileBlob || !filename) throw new Error("文件和文件名不能为空");
  const rsaPublicKeyPEM = PUBLIC_KEY;
  if (!rsaPublicKeyPEM) throw new Error("RSA公钥未初始化");

  const { aesKey, aesIv } = AESUtils.generateKeyAndIv();
  const encryptedFileBytes = await encryptFileBlob(fileBlob, aesKey, aesIv);
  const encryptedExtraFields = {};
  for (const [key, value] of Object.entries(extraFields)) {
    if (typeof value === 'string') {
      encryptedExtraFields[`${key}_encrypted`] = AESUtils.encrypt(value, aesKey, aesIv);
    }
  }
  const aesKeyIv = `${aesKey}|${aesIv}`;
  const encryptedKey = rsaEncryptKeyNative(aesKeyIv, rsaPublicKeyPEM);
  const formData = new FormData();
  formData.append("avatarfile", new Blob([encryptedFileBytes], { type: fileBlob.type }), filename);
  formData.append("encryptedKey", encryptedKey);
  for (const [key, value] of Object.entries(encryptedExtraFields)) {
    formData.append(key, value);
  }
  return formData;
}
